/////////////////////////////////////////////////////////////////////////////////////////////
///<Filename>  		SceneSystem.h		<\Filename>
///<Date>      		09/26/2011			<\Date>      
///<Author>   		Richard A. Small	<\Author>   
///<Purpose>   	
///		A stack-based state system to control the game scenes
///<\Purpose>
/////////////////////////////////////////////////////////////////////////////////////////////
#pragma once

#include "../Main/Standard.h"
#include "../Scene/IScene.h"
#include "../Utility/Utility.h"
#include "../Utility/HashString.h"
#include "../Container/DLList.h"
#include "../Container/Dictionary.h"
#include "../Container/DynamicArray.h"

class SceneSystem
{
public:

	/// <Summary> The nullsceneid </Summary>
	static const HashString NULLsceneID;

private:

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Values that represent GameSceneCmdType  </Summary>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	enum GameSceneCmdType
	{
		PUSH,
		CHANGE,
		POP,
		FIRST
	};

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Game scene command  </Summary>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	struct GameSceneCmd
	{
		GameSceneCmdType cmd;
		const HashString* sceneID;
		float delay;

		GameSceneCmd() { }
		GameSceneCmd(GameSceneCmdType _cmd, const HashString* _sceneID, float _delay)
			: cmd(_cmd), sceneID(_sceneID), delay(_delay) { }
	};

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Defines an alias representing the scene register map </Summary>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	typedef Dictionary<unsigned long, IScene*> SceneRegisterMap;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Defines an alias representing queue of commands </Summary>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	typedef DLList<GameSceneCmd> CommandQueue;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Defines an alias representing list of scenes </Summary>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	typedef DynamicArray<IScene*> SceneList;

	/// <Summary> List of scenes that have registered </Summary>
	SceneRegisterMap registeredScenes;

	/// <Summary> List of scenes currently on the stack </Summary>
	SceneList sceneStack;

	/// <Summary> Queue of all current commands </Summary>
	CommandQueue cmdQueue;

private:

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Makes copy and assignment unavailable to this class </Summary>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	DISALLOW_COPY_AND_ASSIGN(SceneSystem)

	SceneSystem() : registeredScenes(Utility::HashFuncULong) { }
	~SceneSystem() { }

	/// <Summary> The instance of this class </Summary>
	static SceneSystem* instance;

public:

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Gets the instance of this class </Summary>
	///
	/// <Return>	The instance of this class </Return>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	static SceneSystem& GetInstance();

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Deletes the instance of this class </Summary>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	static void DeleteInstance();


private:

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Sends a command </Summary>
	///
	/// <Param name="cmd">	  	The command </Param>
	/// <Param name="sceneID">	Identifier for the scene </Param>
	/// <Param name="delay">  	The delay </Param>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void SendCmd(GameSceneCmdType cmd, const HashString* sceneID, float delay)
	{
		cmdQueue.Push_Back(GameSceneCmd(cmd, sceneID, delay));
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Process the commands currently inside the queue </Summary>
	///
	/// <Param name="delta">	Delta time value </Param>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void ProcessCmdQueue(float delta);

public:

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Registers the scene described by ID </Summary>
	///
	/// <Param name="ID">	The identifier </Param>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	template<typename T>
	void RegisterScene(const HashString& ID)
	{
		registeredScenes.Insert(ID.GetIdentity(), new T);
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Gets a scene </Summary>
	///
	/// <Param name="ID">	The identifier </Param>
	///
	/// <Return>	The scene requested </Return>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	template<typename T>
	T& GetScene(const HashString& ID)
	{
		return registeredScenes[ID.GetIdentity()];
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Initialize </Summary>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void Initialize();

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Shutdown </Summary>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void Shutdown();

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Change scene </Summary>
	///
	/// <Param name="ID">   	
	/// 	The identity of the scene you wish to change too
	/// </Param>
	/// <Param name="delay">
	/// 	(optional) The amount of time you wish to delay this command 
	/// </Param>
	/// <Param name="flush">	
	///		(optional) Value whether or not you want all other current commands to stop execution 
	///	</Param>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void ChangeScene(const HashString& ID, float delay = 0.0f, bool flush = false);

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Push scene </Summary>
	///
	/// <Param name="ID">   	
	/// 	The identity of the scene you wish to push
	/// </Param>
	/// <Param name="delay">
	/// 	(optional) The amount of time you wish to delay this command 
	/// </Param>
	/// <Param name="flush">	
	///		(optional) Value whether or not you want all other current commands to stop execution 
	///	</Param>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void PushScene(const HashString& ID, float delay = 0.0f, bool flush = false);

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Pop scene </Summary>
	///
	/// <Param name="numToPop">   	
	/// 	(optional) The number of scenes you wish to pop
	/// </Param>
	/// <Param name="delay">
	/// 	(optional) The amount of time you wish to delay this command 
	/// </Param>
	/// <Param name="flush">	
	///		(optional) Value whether or not you want all other current commands to stop execution 
	///	</Param>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void PopScene(int numToPop = 1, float delay = 0.0f, bool flush = false);

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Updates the scenes on the stack and calls input on the topmost scene </Summary>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void Update(float delta);

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Renders the scenes on the stack </Summary>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void Render(float delta);

public:

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Gets the current scene. </Summary>
	///
	/// <Return>	null if stack is empty, else the current scene. </Return>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	IScene* GetCurrentScene()		{ if (sceneStack.GetSize() == 0) return NULL; return sceneStack[sceneStack.GetSize() - 1]; }

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Query if this stack has a scene change pending </Summary>
	///
	/// <Return>	true if scene change pending, false if not. </Return>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	bool IsSceneChangePending()		{ return (cmdQueue.GetSize() == 0) ? false : true; }

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <Summary>	Gets the current stack size. </Summary>
	///
	/// <Return>	size of the stack </Return>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	unsigned int CurrentStackSize() { return sceneStack.GetSize(); }
};